#pragma once

#include <atomic>
#include <functional>
#include <memory>
#include <mutex>
#include <thread>

#include <include/core/SkMatrix.h>
#include <include/core/SkTypeface.h>
#include <include/core/SkPoint3.h>

class SkBitmap;
class SkCanvas;
class SkSurface;
class ISkiaControl;

class SkiaFramework {
public:
    using DrawFunc = std::function<void(SkCanvas &canvas)>;
    using Draw2Func = std::function<void(SkCanvas &canvas, SkSurface &surface)>;
    using ResizeFunc = std::function<void(int width, int height)>;

    SkiaFramework();
    virtual ~SkiaFramework();

    /* before init */

    // Draw function is nullptr by default.
    // Call order is DrawBefore > DrawFunc > Draw2Func > DrawAfter.
    void SetDrawFunc(const DrawFunc &onDraw);
    void SetDraw2Func(const Draw2Func &onDraw);

    // Resize function is nullptr by default.
    void SetResizeFunc(const ResizeFunc &onResize);

    // Allow GPU by default.
    void SetGPUAllowance(bool allow);

    // Window width is 800 by default.
    void SetWindowWidth(int width);

    // Window height is 640 by default.
    void SetWindowHeight(int height);

    // Window title is "skia framework" by default.
    void SetWindowTitle(const std::string &title);

    /* action */

    int GetWindowWidth() const;
    int GetWindowHeight() const;

    void Run();
    void AddControl(const std::shared_ptr<ISkiaControl> &control);

    /* static action */
    static void DrawString(SkCanvas &canvas, const std::string &str, double x, double y);
    static SkPoint3 MeasureString(const std::string &str);

    /* virtual */

    virtual void DrawBefore(SkCanvas &canvas);
    virtual void DrawAfter(SkCanvas &canvas);
    void DrawColorPicker(SkCanvas &canvas, SkBitmap &bitmap);

private:
    void UIThreadMain();
    void UpdateInvertMatrix();

    DrawFunc onDraw_ = nullptr;
    Draw2Func onDraw2_ = nullptr;
    ResizeFunc onResize_ = nullptr;
    bool allowGPU_ = true;
    int windowWidth_ = 800;
    int windowHeight_ = 640;
    std::string windowTitle_ = "skia framework";
    std::vector<std::shared_ptr<ISkiaControl>> controls;

    void *data_ = nullptr;

    std::unique_ptr<std::thread> uiThread_ = nullptr;
    std::atomic<bool> uiThreadRunning_ = false;

    std::mutex propsMutex_;
    std::atomic<int> x_ = 0;
    std::atomic<int> y_ = 0;
    std::atomic<bool> left_ = false;
    std::atomic<bool> right_ = false;
    std::atomic<bool> click_ = false;
    std::atomic<int> downRX_ = 0;
    std::atomic<int> downRY_ = 0;
    std::atomic<int> diffx_ = 0;
    std::atomic<int> diffy_ = 0;
    std::atomic<int> clickx_ = false;
    std::atomic<int> clicky_ = false;
    SkMatrix mat_;
    SkMatrix invmat_;
};
